﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Diagnostics;

namespace LowLevelGraphics.ColorSpaces
{
    /// <summary>
    /// HSI Channel
    /// </summary>
    public enum HSIChannel
    {
        /// <summary>
        /// 
        /// </summary>
        H = 0,
        /// <summary>
        /// 
        /// </summary>
        S = 1,
        /// <summary>
        /// 
        /// </summary>
        I = 2
    }

    /// <summary>
    /// 
    /// </summary>
    [DebuggerDisplay("S: {m_S}, H: {m_H}, I: {m_I}")]
    public class HSI : AbstractColorSpace
    {
        protected double m_S = 0.0f;
        protected double m_H = 0.0f;
        protected double m_I = 0.0f;

        /// <summary>
        /// primary constructor
        /// </summary>
        /// <param name="_color"></param>
        public HSI(Color _color)
            : base(_color)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="HSI"/> class.
        /// </summary>
        /// <param name="_H">The _ H.</param>
        /// <param name="_S">The _ S.</param>
        /// <param name="_I">The _ I.</param>
        public HSI(double _H, double _S, double _I)
        {
            m_H = _H;
            m_S = _S;
            m_I = _I;
        }

        /// <summary>
        /// used by clone to create a deep copy
        /// </summary>
        /// <param name="_hsi"></param>
        internal HSI(HSI _hsi)
        {
            m_Color = _hsi.m_Color;
            this.m_H = _hsi.m_H;
            this.m_S = _hsi.m_S;
            this.m_I = _hsi.m_I;
        }

        /// <summary>
        /// Enable casting into operator
        /// </summary>
        /// <param name="_color"></param>
        /// <returns></returns>
        public static explicit operator HSI(Color _color)
        {
            return new HSI(_color);
        }

        /// <summary>
        /// Gets or sets H
        /// </summary>
        public double H
        {
            get { return m_H; }
            set { m_H = value; }
        }

        /// <summary>
        /// Gets or sets S
        /// </summary>
        public double S
        {
            get { return m_S; }
            set { m_S = value; }
        }

        /// <summary>
        /// Gets or sets I
        /// </summary>
        public double I
        {
            get { return m_I; }
            set { m_I = value; }
        }

        /// <summary>
        /// used to transform color into this color space
        /// </summary>
        protected override void CalcValues()
        {
           double f = (2 * m_Color.R - m_Color.G - m_Color.B) / (m_Color.G - m_Color.B);
            this.m_S = 1 - 3 / (m_Color.R + m_Color.G + m_Color.B) * ColorFunctions.MinChannel(m_Color);
            this.m_H = 1 / 360 * (90 - Math.Atan(f / Math.Sqrt(3)));
            this.m_I = (m_Color.R + m_Color.G + m_Color.B) / Math.Sqrt(3);
        }

        /// <summary>
        /// transforms back to RGB color space
        /// </summary>
        /// <returns></returns>
        public override Color ToColor()
        {
            double b = 0.0f;
            double g = 0.0f;
            double r = 0.0f;
            if (m_H >= 0 && m_H < 120)
            {
                b = m_I / Math.Sqrt(3) * 1 + (m_S * Math.Cos(m_H - 240) / Math.Cos(300 - m_H));
                g = 1 / Math.Sqrt(3) * (1 - m_S);
                r = Math.Sqrt(3) * m_I - b - g;
            }
            else if (m_H >= 120 && m_H < 240)
            {
                g = m_I / Math.Sqrt(3) * (1 + m_S * Math.Cos(m_H - 120) / (Math.Cos(180 - m_H)));
                r = m_I / Math.Sqrt(3) * (1 - m_S);
                b = Math.Sqrt(3) * m_I - r - g;
            }
            else if (m_H >= 240 && m_H < 360)
            {
                b = m_I / Math.Sqrt(3) * (1 + (m_S * Math.Cos(m_H - 240) / (Math.Cos(300 - m_H))));
                g = m_I / Math.Sqrt(3) * (1 - m_S);
                r = Math.Sqrt(3) * m_I - g - b;
            }

            //ToDO: HSI:  check formulas to eliminate this
            if (r < 0) r = 0; if (r > 255) r = 255;
            if (g < 0) r = 0; if (g > 255) r = 255;
            if (b < 0) r = 0; if (b > 255) r = 255;
            return Color.FromArgb((int)r, (int)g, (int)b);
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return string.Format("Color H = {0}, S = {1}, I = {2}", m_H, m_S, m_I);
        }

        /// <summary>
        /// Gets the values.
        /// </summary>
        /// <returns></returns>
        public override object[] GetValues()
        {
            return new object[] { m_H, m_S, m_I };
        }

        /// <summary>
        /// Sets the values.
        /// </summary>
        /// <param name="_aObject">The _a object.</param>
        public override void SetValues(object[] _aObject)
        {
            this.m_H = (double)_aObject[0];
            this.m_S = (double)_aObject[1];
            this.m_I = (double)_aObject[2];
        }

        /// <summary>
        /// Used to create a deep copy
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new HSI(this);
        }
    }
}
